#coding:utf-8
import json
import re

from django.conf import settings
from django_redis import get_redis_connection
from rest_framework.permissions import  BasePermission

from MSB_ERP.utils.cache_permissions import redis_storage_permissions


class RbacPermission(BasePermission):

    def do_uri(self,url):
        """
        把一个不完整的接口地址，变成一个完整的。/users/---->/api/users/
        """
        base_api=settings.BASE_API
        uri='/'+base_api+'/'+url +'/'
        return re.sub('/+','/',uri)#预防有两个//，把可能有多个//替换成/

    def has_permission(self, request, view):#判断是否有权限
        """
        判断是否有权限，该函数必须要重写
        1，获取请求的url，和请求方法method，user用户对象
        2判断是否白名单的url，或者用户的角色是admin也直接放行，白名单是不需要权限认证的
        3 从redis得到当前登录用户的所有权限
        4 判断是否存在权限
        """
        request_url=request.path
        request_method=request.method
        for safe_url in settings.WHITE_LIST:
            if re.match(settings.REGEX_URL.format(url=safe_url),request_url):
                #用户请求的是白名单的url
                return True
        user=request.user#当前登录的用户
        #获得用户的角色
        role_name_list= user.roles.values_list('name',flat=True)
        if 'admin' in role_name_list:
            return True

        #从redis中获取该用户的所以权限
        redis_coon=get_redis_connection('default')
        if not redis_coon.exists(f'user_{user.id}'): #如果redis没有缓存权限
            redis_storage_permissions(user) #在从mysql中取出，并缓存到redis中

        # 得到所有的权限中key，其中key是接口的url地址
        url_keys=redis_coon.hkeys(f'user_{user.id}')#hkeys获得所有的key
        redis_key=None
        for url_key in url_keys:
            #注意redis中存放是字节数据
            if re.match(settings.REGEX_URL.format(url=self.do_uri(url_key.decode())),request_url):
                #如果redis中的url和请求url是一致的

                redis_key=url_key.decode()

                break
        if redis_key:

            permissions=json.loads(redis_coon.hget(f'user_{user.id}',redis_key).decode())#变成json格式

            for permission in permissions:
                if permission.get('method')==request_method:
                    return True
        else:
             return False



